package com.springframework.aop.scope;

import com.springframework.beans.factory.config.MyBeanDefinition;
import com.springframework.beans.factory.config.MyBeanDefinitionHolder;
import com.springframework.beans.factory.support.MyAbstractBeanDefinition;
import com.springframework.beans.factory.support.MyBeanDefinitionRegistry;
import com.springframework.beans.factory.support.MyRootBeanDefinition;
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * @Author 虎哥
 * @Description //TODO
 * 要带着问题去学习,多猜想多验证
 **/
public abstract class MyScopedProxyUtils {
    private static final String TARGET_NAME_PREFIX = "scopedTarget.";
    private static final int TARGET_NAME_PREFIX_LENGTH = "scopedTarget.".length();

    public MyScopedProxyUtils() {
    }

    public static MyBeanDefinitionHolder createScopedProxy(MyBeanDefinitionHolder definition, MyBeanDefinitionRegistry registry, boolean proxyTargetClass) {
        String originalBeanName = definition.getBeanName();
        MyBeanDefinition targetDefinition = definition.getBeanDefinition();
        String targetBeanName = getTargetBeanName(originalBeanName);
        MyRootBeanDefinition proxyDefinition = new MyRootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setDecoratedDefinition(new MyBeanDefinitionHolder(targetDefinition, targetBeanName));
        proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
        proxyDefinition.setSource(definition.getSource());
        proxyDefinition.setRole(targetDefinition.getRole());
        proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
        if (proxyTargetClass) {
            targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        } else {
            proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
        }

        proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
        proxyDefinition.setPrimary(targetDefinition.isPrimary());
        if (targetDefinition instanceof MyAbstractBeanDefinition) {
            proxyDefinition.copyQualifiersFrom((MyAbstractBeanDefinition) targetDefinition);
        }

        targetDefinition.setAutowireCandidate(false);
        targetDefinition.setPrimary(false);
        registry.registerBeanDefinition(targetBeanName, targetDefinition);
        return new MyBeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }

    public static String getTargetBeanName(String originalBeanName) {
        return "scopedTarget." + originalBeanName;
    }

    public static String getOriginalBeanName(@Nullable String targetBeanName) {
        Assert.isTrue(isScopedTarget(targetBeanName), () -> {
            return "bean name '" + targetBeanName + "' does not refer to the target of a scoped proxy";
        });
        return targetBeanName.substring(TARGET_NAME_PREFIX_LENGTH);
    }

    public static boolean isScopedTarget(@Nullable String beanName) {
        return beanName != null && beanName.startsWith("scopedTarget.");
    }
}
